/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2014-2019.
 * Description: core header for kbox
 * Author: lichaoyue
 * Create: 2014-3-24
 */

#ifndef __KBOX_CORE_H
#define __KBOX_CORE_H

#define KBOX_SUPPORT_MAX_REG_NUM1 (64)
#define KBOX_SUPPORT_MAX_REG_NUM2 (16)
/*
 * kbox file system definition
 */

/* kbox region type */
#define KBOX_IGNORE_SIZE		(0x01)
#define KBOX_MARK_SIZE		(0x00)

/* kbox fs version */
#define KBOX_FS_TYPE_KBOX_VER1			(0x01)
/* kbox fs magic num */
#define KBOX_FS_TYPE_KBOX_MAGIC			(0xA5A6A7A8)

/* kbox logbuf end magic num */
#define KBOX_LOGBUF_END_MAGIC			(0xA5A5A5A5)

#define KBOX_WARNNING_LEN	 (128)

#define KBOX_COMPAT_ADJUST_SIZE  1


/* brief basic fs header of an kbox device file system in device or in memory 24 */
typedef struct tag_kbox_fs_head_basic {
	/* basic info */
	u32 checksum;	/* checksum the fs head. */
	u32 magic_num;	/* kbox fs magic number */
	u32 version;     /* fs version */
	u32 used_size;	/* the size of used device */
	u16 reserved1;  /* fs_flags, only one val : PAGE_ALIGNED */
	u8 reserved0;	/* fs type, now only support mem, no other type */
	u8 byteorder;	/* byteorder */
	u32 data_offset;
} __attribute__((packed)) kbox_fs_head_basic;

/* brief Supper Block of an kbox device file system in device or in memory 36 */
typedef struct tag_kbox_fs_head {
	kbox_fs_head_basic basic;
	/* extend info. according to fs type and version. */
	/* file management */
	u16 file_support;   /* max files support in this dev */
	u16 file_desc_size;
	u16 file_count;     /* total files in this device */
	u16 file_desc_table_offset;   /* offset in bytes */
	u32 file_desc_table_checksum; /* checksum of file_desc_table */
} __attribute__((packed)) kbox_fs_head;

/*
 * brief control Block of an region in device
 *
 * The old kbox_region_desc structure is defined as follows:
 * typedef struct tag_KBOX_REGION_DESC {
 *  char name[KBOX_REGION_NAME_LEN];
 *  u8 index;
 *  u8 flags;
 *  u8 reserved;
 *  u32 offset;
 *  u32 size;
 *  u32 real_size;
 *  u32 log_start;
 *  u32 log_end;
 *  u32 ts_sec;
 * } __attribute__((packed)) kbox_region_desc;
 */
typedef struct tag_KBOX_REGION_DESC {
	char name[KBOX_REGION_NAME_LEN];
	u8 index;
	u8 flags;
#ifdef CONFIG_RTOS_KBOX_REGION_COMPAT
	/*
	 * unlucky...
	 * this is kbox reserved struct, change it cause kbox mem check fail and
	 * lose log very unlucky, on 2014-08-04, kbox feature owner changed it...
	 *
	 * In linux 2.6.34, this structure defines only one byte of reserved space.
	 * This macro is defined in order to maintain the compatibility of the
	 * version upgrade.
	 */
	u8 reserved;
#else
	u8 reserved0;
	u8 reserved1;
#endif
	u32 offset;
	u32 size;      /* the space reserved for this file */
	u32 real_size; /* how many data has written to file. */
	u32 log_start; /* Index into log_buf: next char to be read by an reader */
	u32 log_end;   /* Index into log_buf: most-recently-written-char + 1 */
	/* when we update the file. */
	u32 ts_sec;
} __attribute__((packed)) kbox_region_desc; /* should align to 64 bytes. */

#ifdef CONFIG_RTOS_KBOX_REGIONS_EXTEND
struct dym_region_head {
	u32 file_count;
	u32 file_support;
	u32 page_num;
	u32 file_desc_table_checksum;
	u32 data_offset;
};
#endif

typedef struct tag_kbox_memory {
	char *memory_head;
	unsigned long memory_size;
	/* the supperblock current using */
	kbox_fs_head *fs_head;
	kbox_region_desc *region_table;
	int region_table_len;
	int reserved0; /* if page aligned when allocating, always being 1 */
#ifdef CONFIG_RTOS_KBOX_REGIONS_EXTEND
	struct dym_region_head *dym_head;
	kbox_region_desc *dym_region_table;
#endif
} kbox_memory;

/*
 * get byte order
 * 1: big endian
 * 0: little endian.
 */
#define KBOX_BYTEORDER (cpu_to_be16(0x0A0B) == (0x0A0B) ? 1:0)

/*
 * brief Control Block of an kbox device in memory
 * every time update the fs_head to kbox device, must follow these steps:
 * (1)update the new fs_head.
 * (2)write the new fs_head to device.
 * (3)update fs_head.
 * the process is not reentry.
 */
typedef struct tag_kbox_device_cb {
	/* device related */
	u32 dev_status;
	struct kbox_dev_des *dev_desc;
} kbox_device_cb;

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define KBOX_MODULE_NAME_LEN (64 - sizeof(unsigned long))
/*
 * we will recode default level printk
 * KBOX_DEFAULT_LOG_LEVEL == DEFAULT_CONSOLE_LOGLEVEL
 */
typedef struct tag_kbox_region_cb {
	struct list_head list;
	char name[KBOX_REGION_NAME_LEN];
	unsigned int magic; /* just for keep old abi */

	int fd; /* unique id */
	unsigned int size;  /* size of this region */

	unsigned int real_size; /* how many data has written to file. */
	unsigned int log_start;	/* Index into log_buf: next char to be read by an reader */
	unsigned int log_end;   /* Index into log_buf: most-recently-written-char + 1 */

	struct module *module;  /* which module belongs to */
	char module_name[KBOX_MODULE_NAME_LEN];

	char *log_buf;

	/* logbuf_lock protects this cb. */
	spinlock_t logbuf_lock;

	struct proc_dir_entry *proc_entry;
	struct proc_dir_entry *proc_entrycfg;

	kbox_region_desc *pregion_des;
	bool is_history; /* rw: new region;  readonly: history region */
	bool is_newline;
	atomic_t proc_read_count;
	unsigned int fpos;
	unsigned int part_copy;
	bool reset_fpos;
	bool forbidden_record_time;
} kbox_region_cb;

extern struct semaphore	kbox_sem;

extern struct list_head kbox_region_list;

extern void kbox_erase_regions(void);

extern int kbox_region_read(kbox_region_cb *cb, char __user *buffer, size_t buflen);

extern kbox_region_cb *kbox_region_find(int rfd);
extern int kbox_get_region(int rfd, kbox_region_cb **pcb);
extern void kbox_put_region(kbox_region_cb *cb);

extern int kbox_create_default_reg(void);

extern void zero_log_buf(kbox_region_cb *cb, int do_dump, char *warn_str);
extern int wakeup_kboxd(void *start, unsigned long len, char *warnstr, char *region_name);
extern int kbox_core_init(void);
extern int kbox_core_fini(void);
extern kbox_memory kbox_memory_list;
extern kbox_device_cb  kbox_dev_cb;
extern kbox_region_cb *kbox_default_region;
extern struct module *kbox_module;
kbox_region_cb *kbox_region_find_by_name(const char *name);

extern int kbox_compat_value;

static inline unsigned int kbox_region_desc_size(void)
{
	return sizeof(kbox_region_desc) + kbox_compat_value;
}

static inline u32 get_desc_offset(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->offset) + kbox_compat_value);
}

static inline u32 set_desc_offset(kbox_region_desc *desc, u32 offset)
{
	return *(u32 *)((char *)(&desc->offset) + kbox_compat_value) = offset;
}

static inline u32 get_desc_size(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->size) + kbox_compat_value);
}

static inline u32 set_desc_size(kbox_region_desc *desc, u32 desc_size)
{
	return *(u32 *)((char *)(&desc->size) + kbox_compat_value) = desc_size;
}

static inline u32 get_desc_real_size(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->real_size) + kbox_compat_value);
}

static inline u32 set_desc_real_size(kbox_region_desc *desc, u32 real_size)
{
	return *(u32 *)((char *)(&desc->real_size) + kbox_compat_value) = real_size;
}

static inline u32 get_desc_log_start(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->log_start) + kbox_compat_value);
}

static inline u32 set_desc_log_start(kbox_region_desc *desc, u32 log_start)
{
	return *(u32 *)((char *)(&desc->log_start) + kbox_compat_value) = log_start;
}

static inline u32 get_desc_log_end(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->log_end) + kbox_compat_value);
}

static inline u32 set_desc_log_end(kbox_region_desc *desc, u32 log_end)
{
	return *(u32 *)((char *)(&desc->log_end) + kbox_compat_value) = log_end;
}

static inline u32 get_desc_ts_sec(kbox_region_desc *desc)
{
	return *(u32 *)((char *)(&desc->ts_sec) + kbox_compat_value);
}

static inline u32 set_desc_ts_sec(kbox_region_desc *desc, u32 ts_sec)
{
	return *(u32 *)((char *)(&desc->ts_sec) + kbox_compat_value) = ts_sec;
}

static inline kbox_region_desc *get_region_desc(int id)
{
	return (kbox_region_desc *)((char *)kbox_memory_list.region_table +
				    id * kbox_region_desc_size());
}

static inline kbox_region_desc *get_last_region_desc(void)
{
	return get_region_desc(kbox_memory_list.fs_head->file_count - 1);
}

extern bool check_kbox_region_desc_single(kbox_region_desc *region, u32 data_offset,
	char *warning_str, int *warning_str_len);
extern u32 kbox_checksum(u8 *buff, int size);
extern void update_checksum(void);
extern int kbox_region_insert(kbox_region_cb *cb);
extern kbox_region_cb *kbox_region_malloc(const char *name, u32 size, kbox_region_desc *log_des,
	struct module *module, bool is_history);
extern int create_region_desc(kbox_region_desc *region);
#endif
